import os
import importlib
from typing import List, Optional
from quart import Blueprint, Quart
from loguru import logger

class BlueprintLoader:
    """蓝图自动加载器"""
    
    def __init__(self, app: Optional[Quart] = None):
        self.app = app
        self.loaded_blueprints: List[Blueprint] = []
    
    def init_app(self, app: Quart):
        """初始化应用"""
        self.app = app
    
    def load_blueprints(self, routes_dir: str = "src/route") -> List[Blueprint]:
        """自动加载所有蓝图
        
        Args:
            routes_dir: 路由目录路径
            
        Returns:
            加载的蓝图列表
        """
        if not self.app:
            raise RuntimeError("应用未初始化，请先调用 init_app()")
        
        blueprints = []
        
        # 确保路由目录存在
        if not os.path.exists(routes_dir):
            logger.warning(f"路由目录不存在: {routes_dir}")
            return blueprints
        
        logger.info(f"开始扫描路由目录: {routes_dir}")
        
        # 遍历路由目录
        for root, dirs, files in os.walk(routes_dir):
            # 跳过 __pycache__ 目录
            dirs[:] = [d for d in dirs if not d.startswith('__pycache__')]
            
            for file in files:
                if self._is_route_file(file):
                    blueprint = self._load_blueprint_from_file(root, file)
                    if blueprint:
                        blueprints.append(blueprint)
        
        self.loaded_blueprints = blueprints
        logger.info(f"蓝图加载完成，共加载 {len(blueprints)} 个蓝图")
        
        return blueprints
    
    def _is_route_file(self, filename: str) -> bool:
        """判断是否为路由文件
        
        Args:
            filename: 文件名
            
        Returns:
            是否为路由文件
        """
        return (
            (
                filename.endswith('_route.py') or 
                filename.endswith('.py')
            ) and 
            not filename.startswith('__') and
            filename not in ['_route.py', '__init__.py']
        )
    
    def _load_blueprint_from_file(self, root: str, filename: str) -> Optional[Blueprint]:
        """从文件加载蓝图
        
        Args:
            root: 文件所在目录
            filename: 文件名
            
        Returns:
            加载的蓝图或None
        """
        try:
            # 构建模块路径
            file_path = os.path.join(root, filename)
            relative_path = os.path.relpath(file_path, '.')
            module_name = relative_path.replace(os.sep, '.').replace('.py', '')
            
            logger.debug(f"尝试加载模块: {module_name}")
            
            # 动态导入模块
            module = importlib.import_module(module_name)
            
            # 查找蓝图对象
            blueprint = self._find_blueprint_in_module(module, filename)
            
            if blueprint:
                # 注册蓝图到应用
                self.app.register_blueprint(blueprint)
                
                logger.info(
                    f"成功注册蓝图: {blueprint.name} -> {blueprint.url_prefix or '/'} "
                    f"(来源: {module_name})"
                )
                
                return blueprint
            else:
                logger.warning(f"在模块 {module_name} 中未找到蓝图对象")
                
        except ImportError as e:
            logger.error(f"导入模块失败: {module_name}, 错误: {e}")
        except Exception as e:
            logger.error(f"加载蓝图失败: {module_name}, 错误: {e}")
        
        return None
    
    def _find_blueprint_in_module(self, module, filename: str) -> Optional[Blueprint]:
        """在模块中查找蓝图对象
        
        Args:
            module: Python模块对象
            filename: 文件名
            
        Returns:
            找到的蓝图对象或None
        """
        # 优先查找与文件名匹配的蓝图变量
        blueprint_name = filename.replace('.py', '')
        if hasattr(module, blueprint_name):
            attr = getattr(module, blueprint_name)
            if isinstance(attr, Blueprint):
                return attr
        
        # 查找所有蓝图对象
        blueprints = []
        for attr_name in dir(module):
            if not attr_name.startswith('_'):  # 跳过私有属性
                attr = getattr(module, attr_name)
                if isinstance(attr, Blueprint):
                    blueprints.append(attr)
        
        if len(blueprints) == 1:
            return blueprints[0]
        elif len(blueprints) > 1:
            logger.warning(
                f"模块 {module.__name__} 中发现多个蓝图对象: "
                f"{[bp.name for bp in blueprints]}，将使用第一个"
            )
            return blueprints[0]
        
        return None
    
    def get_loaded_blueprints(self) -> List[Blueprint]:
        """获取已加载的蓝图列表"""
        return self.loaded_blueprints.copy()
    
    def get_blueprint_info(self) -> List[dict]:
        """获取蓝图信息
        
        Returns:
            蓝图信息列表
        """
        info = []
        for blueprint in self.loaded_blueprints:
            info.append({
                'name': blueprint.name,
                'url_prefix': blueprint.url_prefix or '/',
                'routes': self._get_blueprint_routes(blueprint)
            })
        return info
    
    def _get_blueprint_routes(self, blueprint: Blueprint) -> List[dict]:
        """获取蓝图的路由信息
        
        Args:
            blueprint: 蓝图对象
            
        Returns:
            路由信息列表
        """
        routes = []
        
        # Quart框架直接获取路由信息，不需要应用上下文
        if self.app:
            try:
                for rule in self.app.url_map.iter_rules():
                    if rule.endpoint and rule.endpoint.startswith(f"{blueprint.name}."):
                        routes.append({
                            'endpoint': rule.endpoint,
                            'rule': rule.rule,
                            'methods': list(rule.methods - {'HEAD', 'OPTIONS'})
                        })
            except Exception as e:
                logger.warning(f"获取蓝图 {blueprint.name} 路由信息失败: {e}")
        
        return routes
    
    def reload_blueprints(self, routes_dir: str = "src/route") -> List[Blueprint]:
        """重新加载蓝图（开发环境使用）
        
        Args:
            routes_dir: 路由目录路径
            
        Returns:
            重新加载的蓝图列表
        """
        logger.info("开始重新加载蓝图...")
        
        # 清除已加载的蓝图
        self.loaded_blueprints.clear()
        
        # 重新加载
        return self.load_blueprints(routes_dir)

# 全局蓝图加载器实例
blueprint_loader = BlueprintLoader()

# 便捷函数
def init_blueprints(app: Quart, routes_dir: str = "src/route") -> List[Blueprint]:
    """初始化并加载所有蓝图
    
    Args:
        app: Quart应用实例
        routes_dir: 路由目录路径
        
    Returns:
        加载的蓝图列表
    """
    blueprint_loader.init_app(app)
    return blueprint_loader.load_blueprints(routes_dir)

def get_blueprint_info() -> List[dict]:
    """获取蓝图信息"""
    return blueprint_loader.get_blueprint_info()